iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 21
0

大家的三個重點

Henry

kai

  • 謹記 JS Equality Table,避免遇到 bug
  • 善用 || 與 && 寫出簡短程式碼

Lai
1.一個&&或||操作符產生的值不見得是Boolean類型。這個產生的值將總是兩個操作數表達式其中之一的值
2.如果Type(x)是Boolean, 返回比較ToNumber(x) == y 的結果。如果Type(y)是Boolean, 返回比較x == ToNumber(y) 的結果。

Andy:

  1. && 跟 || 運算子
  2. == 跟 === 都會比較值跟型別,== 會在判斷是否強制轉型
  3. falsy 值與 falsy 值比較的雷

Chris: 轉型相等 守護運算子 別用 ==false

日安:

  1. 明辨寬鬆相等和嚴格相等
  2. 物件隱含轉型後會變成物件包裹器

turtle:

  1. || 和 && 運算子
  2. == 要注意轉型

Jimmy

  1. == 允許相等性比較中的的強制轉型,而 === 不允許強制轉型
  2. 永遠都別使用 == true 或 == false。
  3. JavaScript 中的相等性

mango:

  1. 運算子 || 與 && 的回傳結果都是值
a || b;
// 大略等同於
a ? a : b;

a && b;
// 大略等同於
a ? b : a;
  1. == 允许强制轉型,而 === 不允许强制轉型

共筆筆記

隱含的強制轉型

隱含的強制轉型:不明顯的任何型別的轉換動作。

絕大部分是因為==,覺得它讓程式碼更難理解,但我們的目標應該是減少冗贅、反覆套用,以及非必要的實作,以專注更重要的目的。

簡化隱含

以強型別的類似虛擬程式碼:

SomeType x = SomeType( AnotherType( y ) )

但如果可以這樣:

SomeType x = SomeType( y )

這樣做的優點:

  • 降低中介轉換步驟
  • 增加程式碼的可讀性

而 JavaScript 就是有類似這樣的輔助,在隱含轉型也有優點可用。

隱含地:Strings <--> Numbers

var a = "42";
var b = "0";

var c = 42;
var d = 0;

a + b; // "420"
c + d; // 42

一樣都是+運算子,那什麼導致兩者運算結果不同?他要如何知道是字串的相接還是數值的運算?

再看看這個:

var a = [1,2];
var b = [3,4];

a + b; // "1,23,4"

這兩個數值都不是字串,然而他們進行字串的串接。

如果有任一運算元已經是一個字串,或是一個字串表示值,那麼使用+就會進行字串的相接。

轉化步驟:

  • + 接收到 object 或 Array
  • 呼叫 ToPrimitive 抽象運算
    • valueOf 無法產生基型值
    • toString 做串接
  • 呼叫[[DefalutValue]]演算法
  • 根據 context 傳入 number 作為 hint

+ 的任一運算元是字串,那進行的就會是字串的串接,不然就是數值的加法運算。

var a = 42;
var b = a + "";

b; // "42"

所以只要加上空字串就可以強制轉型把數字轉成字串,其步驟:

  • 調用 valueOf
  • 回傳值透過內部的 ToString 被轉為字串

但如果使用物件,得到的結果不一定會得到相同值。
反過來,如何將字串隱含強制轉型為字串呢?

var a = "3.14";
var b = a - 0;

b; // 3.14

- 只被定義做數值的減法運算,所以運算使a被迫強制轉型,a/1a*1也可以達成相同效果,那如果是 object 值呢?

var a = [3];
var b = [1];

a - b; // 2

這兩個陣列值都必須變成數字,他們會先變成字串,在強制轉型成數字,以進行數值運算。

隱含地:Booleans <--> Numbers

可以把特定類型複雜的布林值轉換:

function onlyOne(a,b,c) {
	return !!((a && !b && !c) ||
		(!a && b && !c) || (!a && !b && c));
}

var a = true;
var b = false;

onlyOne( a, b, b );	// true
onlyOne( b, a, b );	// true

onlyOne( a, b, a );	// false

function onlyOne() {
	var sum = 0;
	for (var i=0; i < arguments.length; i++) {
		// 跳过falsy值。与将它们视为0相同,但是避开NaN
		if (arguments[i]) {
			sum += arguments[i];
		}
	}
	return sum == 1;
}

var a = true;
var b = false;

onlyOne( b, a );				// true
onlyOne( b, a, b, b, b );		// true

onlyOne( b, b );				// false
onlyOne( b, a, b, b, b, a );	// false

或使用明確的強制轉型:

function onlyOne() {
	var sum = 0;
	for (var i=0; i < arguments.length; i++) {
		sum += Number( !!arguments[i] );
	}
	return sum === 1;
}

隱含地:* <--> Booleans

  1. 在一個if (..)述句中的測試運算式。
  2. 在一個for ( .. ; .. ; .. )標頭的測試運算式(第二個子句)。
  3. 在while (..)和do..while(..)迴圈中的測試運算式。
  4. 在? :三元表達式中的測試表達式(第一個子句)。
  5. ||(邏輯或)和&&(邏輯與)運算子左手邊的運算元。
var a = 42;
var b = "abc";
var c;
var d = null;

if (a) {
	console.log( "yep" );		// yep
}

while (c) {
	console.log( "nope, never runs" );
}

c = d ? a : b;
c;								// "abc"

if ((a && d) || c) {
	console.log( "yep" );		// yep
}

以上非 boolean 的值都會隱含的強制轉型。

運算子 || 與 &&

這兩個運算子並不會產生一個邏輯值(boolean),而是他們選擇兩個之中其一的運算元:

var a = 42;
var b = "abc";
var c = null;

a || b;		// 42
a && b;		// "abc"

c || b;		// "abc"
c && b;		// null
  • ||||會在第一個運算元進行布林的測試,如果運算元不是布林,就會強制轉型。
  • ||來說:
    • 測試結果是true,該運算元的結果就是第一個運算元(a 或 c)
    • 測試結果是false,該運算元的結果就是第二個運算元(b)
  • &&來說:
    • 測試結果是true,該運算元的結果就是第二個運算元(b)
    • 測試結果是false,該運算元的結果就是第一個運算元(a 或c)

一個||&&的值永遠會是其中一個運算元底層的值,而非測試結果。

或許可以這樣記:

a || b;
// 大略等同於:
a ? a : b;

a && b;
// 大略等同於:
a ? b : a;

有個很常用的方法:

function foo(a,b) {
	a = a || "hello";
	b = b || "world";

	console.log( a + " " + b );
}

foo();					// "hello world"
foo( "yeah", "yeah!" );	// "yeah yeah!"

||常常做備用的預設值,但要小心:

foo( "That's it!", "" ); // "That's it! world" <-- Oops!

這個狀況,我們預期應該是That's it!,但因為第二個引數是falsy,所以預設值被替換上去。
如果是這樣,一是確保引數都要是truthy,不然可以使用三元運算式。

如果是&&,因為第一個運算子為truthy,才會選擇第二個運算子,所以又稱守護運算子,第一個運算式的測試會守護第二個運算式。

function foo() {
	console.log( a );
}

var a = 42;

a && foo(); // 42

這個狀況就是a是 truthy 的時候才會呼叫foo();

以復合運算式來說:

var a = 42;
var b = null;
var c = "foo";

if (a && (b || c)) {
	console.log( "yep" );
}
  • b || c :回傳'foo'
  • a && 'foo': 回傳 foo
  • foo 強制轉型為 true

Symbol 的強制轉型

var s1 = Symbol( "cool" );
String( s1 );	// "Symbol(cool)"

var s2 = Symbol( "not cool" );
s2 + "";        // TypeError
  • symbol 允許明確的強制轉型為字串
  • symbol 禁止隱含的強制轉型

上一篇
Day20:JavaScript 優良部份 Chapter02 文法 筆記精要
下一篇
Day22:YDKJS 第五次讀書會(下)
系列文
寇丁人妻的前端書蟲日誌30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言